package com.dev.security.util;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.data.jpa.domain.Specification;

/**
 * @author 潜行的青衣
 * @see
 */
public class SpecificationUtils {

	public static <T> Path<T> getPath(Root<?> root, String field) {
		Path<T> path = null;
		if(field.indexOf(".") > -1) {
			Path<?> t_path = null;
			String[] fields = field.split("\\.");
			int len = fields.length - 1;
			for(int i=0; i<fields.length; i++) {
				if(i < len) {
					if(t_path == null) {
						t_path = root.get(fields[i]);
					} else {
						t_path = t_path.get(fields[i]);
					}
				} else {
					if(t_path == null) {
						path = root.get(fields[i]);
					} else {
						path = t_path.get(fields[i]);
					}
				}
			}
		} else {
			path = root.get(field);
		}
		return path;
	}
	
	public static <T> Specification<T> eq(final String field, final Object value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				return cb.equal(getPath(root, field), value);
			}
			
		};
	}
	
	public static <T> Specification<T> gt(final String field, final Number value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<? extends Number> path = getPath(root, field);
				return cb.gt(path, value);
			}
			
		};
	}
	
	public static <T, Y extends Comparable<? super Y>> Specification<T> greaterThan(final String field, final Y value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<Y> path = getPath(root, field);
				return cb.greaterThan(path, value);
			}
			
		};
	}
	
	public static <T, Y extends Comparable<? super Y>> Specification<T> greaterThanOrEqualTo(final String field, final Y value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<? extends Y> path = getPath(root,field);
				return cb.greaterThanOrEqualTo(path, value);
			}
			
		};
	}
	
	
	public static <T> Specification<T> ge(final String field, final Number value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<? extends Number> path = getPath(root,field);
				return cb.ge(path, value);
			}
			
		};
	}
	
	public static <T> Specification<T> lt(final String field, final Number value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<? extends Number> path = getPath(root, field);
				return cb.lt(path, value);
			}
			
		};
	}
	
	public static <T> Specification<T> le(final String field, final Number value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<? extends Number> path = getPath(root, field);
				return cb.le(path, value);
			}
			
		};
	}
	
	public static <T, Y extends Comparable<? super Y>> Specification<T> lessThan(final String field, final Y value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<Y> path = getPath(root, field);
				return cb.lessThan(path, value);
			}
			
		};
	}
	
	public static <T, Y extends Comparable<? super Y>> Specification<T> lessThanOrEqualTo(final String field, final Y value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<? extends Y> path = getPath(root, field);
				return cb.lessThanOrEqualTo(path, value);
			}
			
		};
	}
	
	public static <T, Y extends Comparable<? super Y>> Specification<T> between(final String field, final Y value1, final Y value2) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<? extends Y> path = getPath(root, field);
				return cb.between(path, value1, value2);
			}
			
		};
	}
	
	
	public static <T> Specification<T> like(final String field, final String value) {
		return new Specification<T>() {
			
			@Override
			public Predicate toPredicate(Root<T> root,
					CriteriaQuery<?> query, CriteriaBuilder cb) {
				Path<String> path = getPath(root, field);
				return cb.like(path, value);
			}
			
		};
	}
	
}
